#include "degfx/driver.h"

#ifdef MX__DRIVER_DJGPP

#   define MXMODULE_DRS

#   include <pc.h>
#   include <dpmi.h>
#   include <go32.h>
#   include <conio.h>
#   include <sys/farptr.h>

extern unsigned mx__djgpp_mouse_valid;
extern unsigned mx__djgpp_pointer(void);
extern unsigned mx__djgpp_key(int *scan, int *ascii);

static void mx__djgpp_vga_palette(void)
{
	unsigned i;

	outportb(0x3C8, 0);

	for (i = 0; i <= 255; i++) {
		unsigned char r = (i & 0xe0) + (i >> 3);
		unsigned char g = ((i & 0x1c) << 3) + (i >> 3);
		unsigned char b = ((i & 0x07) << 6) + (i >> 2);

		outportb(0x3C9, r >> 2);
		outportb(0x3C9, g >> 2);
		outportb(0x3C9, b >> 2);
	}
}

static unsigned mx__djgpp_13h_start(MX_GFX_ARGS * args)
{
	__dpmi_regs r;

	/* Set graphics mode */
	r.x.ax = 0x13;
	__dpmi_int(0x10, &r);

	/* Setup 332 palette for 8 bit modes */
	mx__djgpp_vga_palette();

	/* Reset the mouse driver */
	r.x.ax = 0x0000;
	__dpmi_int(0x33, &r);
	if (r.x.ax)
		mx__djgpp_mouse_valid = true;
	else
		mx__djgpp_mouse_valid = false;

	/* Inform the user of the results */
	args->w = 320;
	args->h = 200;
	args->c = 8;
	args->title = "djgpp_13h";
	args->pointer = false;

	mx_drs_area(args->w - 1, args->h - 1);

	return true;
}

static void mx__djgpp_13h_stop(void)
{
	__dpmi_regs reg;

	reg.x.ax = 0x0003;
	__dpmi_int(0x10, &reg);

	textmode(C80);
}

static void mx__djgpp_13h__flush(const MX_RECT * rect, MX_PIXEL * array, const int stride)
{
	int y = rect->y1;

	/* Set the selector for fast acces to the screen */
	_farsetsel(_dos_ds);

	/* Push the buffer to the visible screen */
	while (y <= rect->y2) {

		unsigned int start = 0xA0000 + (y * 320) + rect->x1;
		unsigned int end = start + (rect->x2 - rect->x1);
		const MX_PIXEL *color = array;

		while (start <= end) {
			_farnspokeb(start++, MXRGB332(*color));
			++color;
		}

		array += stride;
		++y;
	}
}

static void mx__djgpp_13h_flush(MX_RECT * rect)
{
	mx__gfx_flush(mx__djgpp_13h__flush, rect);
}

static unsigned mx__djgpp_13h_poll(void)
{
	mx_drs_update(mx__djgpp_13h_flush);
	return true;
}

static void mx__djgpp_13h_dirty(const MX_RECT * rect)
{
	mx_drs_dirty(rect, true);
}

const MX_DRIVER mx__djgpp_13h = {
	mx__djgpp_13h_start,
	mx__djgpp_13h_stop,

	mx__djgpp_13h_poll,
	mx__djgpp_13h_dirty,

	mx__djgpp_pointer,
	mx__djgpp_key
};

#endif
